//****************************************************
// Porman Project 2
// Filename: CBufferObject.cpp
// Date: 2009/03/21
// Author: KengYu Lin
// Modify:
//****************************************************
#include "CRenderDevice.h"
#include "CBufferObject.h"
#include "CAssert.h"

namespace Porman
{

namespace GL
{

CBufferObject::CBufferObject( CRenderDevice* pDevice, EBufferTarget enumTarget, int iSizeInByte, EBufferUsage enumUsage ) :
CBaseObject(pDevice)
{
    glGenBuffers( 1, &m_uiObjID );
    if( m_uiObjID != 0 )
    {
        setName("Non");
        m_eTarget = enumTarget;
        m_eUsage = enumUsage;

        glBindBuffer( m_eTarget, m_uiObjID );
        glBufferData( m_eTarget, (unsigned int)iSizeInByte, NULL, m_eUsage );
        if( glGetError() == GL_OUT_OF_MEMORY )
        {
            glDeleteBuffers( 1, &m_uiObjID );
            throw 0;
        }
       
        m_iSizeInBytes = iSizeInByte;
        m_iNumOfBufferObj++;
        glBindBuffer( m_eTarget, 0 );
    }
    else
        throw 0;
}

CBufferObject::~CBufferObject( void )
{
    if( m_uiObjID != 0 )
    {
        glDeleteBuffers( 1, &m_uiObjID );
        m_uiObjID = 0;
    }

    m_iNumOfBufferObj--;
}

void CBufferObject::changeTarget( EBufferTarget enumTarget ) 
{ 
    if( m_eTarget != enumTarget )
    {
        m_eTarget = enumTarget;
    }
}

ERESULT CBufferObject::setData( int iOffset, int iSize, const void* pData )
{
    THREAD_CHECKING(m_pDeviceRef->getCurThreadID(), E_THREAD_ERROR);

    char* pSrc = NULL;
    ERESULT enumResult = _lock( E_WRITE_ONLY, iOffset, (void**)(&pSrc) );
    if( enumResult == E_SUCCESS )
    {
        memcpy( pSrc, pData, iSize );
        enumResult = _unlock();
    }

    if( enumResult != E_SUCCESS )
        assert(0);

    return enumResult;
}

ERESULT CBufferObject::getData( int iOffset, int iSize, void* pData )
{
    THREAD_CHECKING(m_pDeviceRef->getCurThreadID(), E_THREAD_ERROR);

    char* pSrc = NULL;
    ERESULT enumResult = _lock( E_READ_ONLY, iOffset, (void**)(&pSrc) );
    if( enumResult == E_SUCCESS )
    {
        if( iSize > (m_iSizeInBytes - iOffset) )
            iSize = m_iSizeInBytes - iOffset;

        memcpy( pData, pSrc, iSize );
        enumResult = _unlock();
    }

    if( enumResult != E_SUCCESS )
        assert(0);

    return enumResult;
}

ERESULT CBufferObject::_lock( EBufferAccess enumAccess, int iOffset, void** ppData )
{
    THREAD_CHECKING(m_pDeviceRef->getCurThreadID(), E_THREAD_ERROR);

    glBindBuffer( m_eTarget, m_uiObjID );
    glBufferData( m_eTarget, (unsigned int)m_iSizeInBytes, NULL, m_eUsage );

    (*ppData) = glMapBuffer( m_eTarget, enumAccess );
    if( (*ppData) )
        (*ppData) = (char*)(*ppData) + (unsigned int)iOffset;
    else
    {
        switch( glGetError() )
        {
        case GL_OUT_OF_MEMORY:
            return E_OUT_OF_MEMORY;
        case GL_INVALID_OPERATION:
        default:
            return E_INVALID_OPERATION;
        }
    }

    return E_SUCCESS;
}

ERESULT CBufferObject::_unlock( void )
{
    THREAD_CHECKING(m_pDeviceRef->getCurThreadID(), E_THREAD_ERROR);

    if( glUnmapBuffer( m_eTarget ) )
    {
        glBindBuffer( m_eTarget, 0 );
        return E_SUCCESS;
    }

    return E_INVALID_OPERATION;
}

}//End namespace GL

}//End namespace Porman